﻿#region COPYRIGHT© 2005-2013 Phillip Clark. All rights reserved.

// For licensing information see License.txt (MIT style licensing).

#endregion

using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;

namespace FlitBit.Parse
{
	/// <summary>
	/// Base class for match rules that apply to characters.
	/// </summary>
	public abstract partial class CharRule : Match
	{
		static readonly int CHashCodeSeed = typeof(CharRule).AssemblyQualifiedName.GetHashCode();

		/// <summary>
		/// Access to the rules.
		/// </summary>
		protected readonly CharMatch[] _rules;

		/// <summary>
		/// Creates a new instance.
		/// </summary>
		/// <param name="kind"></param>
		/// <param name="parts"></param>
		protected CharRule(MatchKind kind, params CharMatch[] parts)
			: base(kind)
		{
			Contract.Requires(parts != null, "parts must not be null");
			Contract.Requires(parts.Length > 0, "parts must be given");

			this._rules = parts;
		}

		/// <summary>
		///   Determines if the instance is equal to another.
		/// </summary>
		/// <param name="obj">the other object</param>
		/// <returns><em>true</em> if equal; otherwise <em>false</em>.</returns>
		public override bool Equals(object obj)
		{
			return obj is CharRule
				&& this.Equals((CharRule) obj);
		}

		/// <summary>
		///   Gets an array of non-variable character matches at the front. The elements
		///   in the returned array are positional such that the item at index 0 represents
		///   an exact character match for the first input character, and so on.
		/// </summary>
		/// <returns></returns>
		public override CharMatch[] GetExactCharacterMatchesOnFront()
		{
			return this._rules.TakeWhile(m => !m.IsVariable).ToArray();
		}

		/// <summary>
		///   Gets the object's hashcode.
		/// </summary>
		/// <returns></returns>
		public override int GetHashCode()
		{
			const int prime = 999067; // a random prime
			var result = CHashCodeSeed * prime;
			result ^= base.GetHashCode() * prime;
			result ^= this._rules.Length * prime;
			foreach (var c in this._rules)
			{
				result ^= c.GetHashCode() * prime;
			}
			return result;
		}

		/// <summary>
		/// Determines if an instance is equal to another.
		/// </summary>
		/// <param name="other">the other</param>
		/// <returns><em>true</em> if equal; otherwise <em>false</em>.</returns>
		public bool Equals(CharRule other)
		{
			if (other != null
				&& base.Equals((Match) other)
				&& this._rules.Length == other._rules.Length)
			{
				for (var i = 0; i < this._rules.Length; i++)
				{
					if (!this._rules[i].Equals(other._rules[i]))
					{
						return false;
					}
				}
				return true;
			}
			return false;
		}
	}
}